/*
*
* This file is part of QMapControl,
* an open-source cross-platform map widget
*
* Copyright (C) 2007 - 2008 Kai Winter
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with QMapControl. If not, see <http://www.gnu.org/licenses/>.
*
* Contact e-mail: kaiwinter@gmx.de
* Program URL   : http://qmapcontrol.sourceforge.net/
*
*/

#include "layer.h"
namespace qmapcontrol
{
	Layer::Layer(QString layername, MapAdapter* mapadapter, enum LayerType layertype, bool takeevents)
	:visible(true), mylayername(layername), mylayertype(layertype), mapAdapter(mapadapter), takeevents(takeevents), myoffscreenViewport(QRect(0,0,0,0))
	{	
// 	qDebug() << "creating new Layer: " << layername << ", type: " << contents;
// 	qDebug() << this->layertype;
	}

	Layer::~Layer()
	{
		delete mapAdapter;
	}

	void Layer::setSize(QSize size)
	{
		this->size = size;
		screenmiddle = QPoint(size.width()/2, size.height()/2);
// 	QMatrix mat;
// 	mat.translate(480/2, 640/2);
// 	mat.rotate(45);
// 	mat.translate(-480/2,-640/2);
// 	screenmiddle = mat.map(screenmiddle);
	
	}

	QString Layer::layername() const
	{
		return mylayername;
	}

	const MapAdapter* Layer::mapadapter() const
	{
		return mapAdapter;
	}

	void Layer::setVisible(bool visible)
	{
		this->visible = visible;
		emit(updateRequest());
	}

	void Layer::addGeometry(Geometry* geom)
	{
// 	qDebug() << geom->getName() << ", " << geom->getPoints().at(0)->getWidget();
	
		geometries.append(geom);
		emit(updateRequest(geom->boundingBox()));
	// a geometry can request a redraw, e.g. when its position has been changed
		connect(geom, SIGNAL(updateRequest(QRectF)),
				  this, SIGNAL(updateRequest(QRectF)));
	}
	void Layer::removeGeometry(Geometry* geometry)
	{
		for (int i=0; i<geometries.count(); i++)
		{
			if (geometry == geometries.at(i))
			{
				disconnect(geometry);
				geometries.removeAt(i);
// 			delete geometry;
			}
		}
	}
	
	void Layer::clearGeometries()
	{
		foreach(Geometry *geometry, geometries){
			disconnect(geometry);
		}
		geometries.clear();
	}	
	
	bool Layer::isVisible() const
	{
		return visible;
	}
	void Layer::zoomIn() const
	{
		mapAdapter->zoom_in();
	}
	void Layer::zoomOut() const
	{
		mapAdapter->zoom_out();
	}

	void Layer::mouseEvent(const QMouseEvent* evnt, const QPoint mapmiddle_px)
	{
		if (takesMouseEvents())
		{
			if (evnt->button() == Qt::LeftButton && evnt->type() == QEvent::MouseButtonPress)
			{		
			// check for collision
				QPointF c = mapAdapter->displayToCoordinate(QPoint(evnt->x()-screenmiddle.x()+mapmiddle_px.x(),
						evnt->y()-screenmiddle.y()+mapmiddle_px.y()));
				Point* tmppoint = new Point(c.x(), c.y());
				for (int i=0; i<geometries.count(); i++)
				{
					if (geometries.at(i)->isVisible() && geometries.at(i)->Touches(tmppoint, mapAdapter))
				
// 				if (geometries.at(i)->Touches(c, mapAdapter))
					{
						emit(geometryClicked(geometries.at(i), QPoint(evnt->x(), evnt->y())));
					}
				}
				delete tmppoint;
			}
		}
	}

	bool Layer::takesMouseEvents() const
	{
		return takeevents;
	}

	void Layer::drawYourImage(QPainter* painter, const QPoint mapmiddle_px) const
	{
		if (mylayertype == MapLayer)
		{
// 			qDebug() << ":: " << mapmiddle_px;
// 			QMatrix mat;
// 			mat.translate(480/2, 640/2);
// 			mat.rotate(45);
// 			mat.translate(-480/2,-640/2);
			
// 			mapmiddle_px = mat.map(mapmiddle_px);
// 			qDebug() << ":: " << mapmiddle_px;
			_draw(painter, mapmiddle_px);
		}

		drawYourGeometries(painter, QPoint(mapmiddle_px.x()-screenmiddle.x(), mapmiddle_px.y()-screenmiddle.y()), myoffscreenViewport);
	}
	void Layer::drawYourGeometries(QPainter* painter, const QPoint mapmiddle_px, QRect viewport) const
	{
		QPoint offset;
		if (mylayertype == MapLayer)
			offset = mapmiddle_px;
		else
			offset = mapmiddle_px-screenmiddle;
	
		painter->translate(-mapmiddle_px+screenmiddle);
		for (int i=0; i<geometries.count(); i++)
		{
			geometries.at(i)->draw(painter, mapAdapter, viewport, offset);
		}
		painter->translate(mapmiddle_px-screenmiddle);

	}
	void Layer::_draw(QPainter* painter, const QPoint mapmiddle_px) const
	{
	// screen middle rotieren...

		int tilesize = mapAdapter->tilesize();
		int cross_x = int(mapmiddle_px.x())%tilesize;		// position on middle tile
		int cross_y = int(mapmiddle_px.y())%tilesize;
// 	qDebug() << screenmiddle << " - " << cross_x << ", " << cross_y;
		
		// calculate how many surrounding tiles have to be drawn to fill the display
		int space_left = screenmiddle.x() - cross_x;
		int tiles_left = space_left/tilesize;
		if (space_left>0) 
			tiles_left+=1;
	
		int space_above = screenmiddle.y() - cross_y;	
		int tiles_above = space_above/tilesize;
		if (space_above>0) 
			tiles_above+=1;
			
		int space_right = screenmiddle.x() - (tilesize-cross_x);
		int tiles_right = space_right/tilesize;
		if (space_right>0) 
			tiles_right+=1;
			
		int space_bottom = screenmiddle.y() - (tilesize-cross_y);
		int tiles_bottom = space_bottom/tilesize;
		if (space_bottom>0) 
			tiles_bottom+=1;
			
// 	int tiles_displayed = 0;
		int mapmiddle_tile_x = mapmiddle_px.x()/tilesize;
		int mapmiddle_tile_y = mapmiddle_px.y()/tilesize;

		const QPoint from =	QPoint((-tiles_left+mapmiddle_tile_x)*tilesize, (-tiles_above+mapmiddle_tile_y)*tilesize);
		const QPoint to =		QPoint((tiles_right+mapmiddle_tile_x+1)*tilesize, (tiles_bottom+mapmiddle_tile_y+1)*tilesize);

		myoffscreenViewport = QRect(from, to);
	
		if (mapAdapter->isValid(mapmiddle_tile_x, mapmiddle_tile_y, mapAdapter->currentZoom()))
		{
			painter->drawPixmap(-cross_x+size.width(),
										-cross_y+size.height(),
												ImageManager::instance()->getImage(mapAdapter->host(), mapAdapter->query(mapmiddle_tile_x, mapmiddle_tile_y, mapAdapter->currentZoom())));
		}
	
		for (int i=-tiles_left+mapmiddle_tile_x; i<=tiles_right+mapmiddle_tile_x; i++)
		{
			for (int j=-tiles_above+mapmiddle_tile_y; j<=tiles_bottom+mapmiddle_tile_y; j++)
			{
				// check if image is valid
				if (!(i==mapmiddle_tile_x && j==mapmiddle_tile_y))
					if (mapAdapter->isValid(i, j, mapAdapter->currentZoom()))
				{
				
					painter->drawPixmap(((i-mapmiddle_tile_x)*tilesize)-cross_x+size.width(),
												 ((j-mapmiddle_tile_y)*tilesize)-cross_y+size.height(),
													ImageManager::instance()->getImage(mapAdapter->host(), mapAdapter->query(i, j, mapAdapter->currentZoom())));
// 			if (QCoreApplication::hasPendingEvents())
// 				QCoreApplication::processEvents();
				}
			}
		}
	
	
	// PREFETCHING
		int upper = mapmiddle_tile_y-tiles_above-1;
		int right = mapmiddle_tile_x+tiles_right+1;
		int left = mapmiddle_tile_x-tiles_right-1;
		int lower = mapmiddle_tile_y+tiles_bottom+1;

		int j = upper;
		for (int i=left; i<=right; i++)
		{
			if (mapAdapter->isValid(i, j, mapAdapter->currentZoom()))
				ImageManager::instance()->prefetchImage(mapAdapter->host(), mapAdapter->query(i, j, mapAdapter->currentZoom()));
		}
		j = lower;
		for (int i=left; i<=right; i++)
		{
			if (mapAdapter->isValid(i, j, mapAdapter->currentZoom()))
				ImageManager::instance()->prefetchImage(mapAdapter->host(), mapAdapter->query(i, j, mapAdapter->currentZoom()));
		}
		int i = left;
		for (int j=upper+1; j<=lower-1; j++)
		{
			if (mapAdapter->isValid(i, j, mapAdapter->currentZoom()))
				ImageManager::instance()->prefetchImage(mapAdapter->host(), mapAdapter->query(i, j, mapAdapter->currentZoom()));
		}
		i = right;
		for (int j=upper+1; j<=lower-1; j++)
		{
			if (mapAdapter->isValid(i, j, mapAdapter->currentZoom()))
				ImageManager::instance()->prefetchImage(mapAdapter->host(), mapAdapter->query(i, j, mapAdapter->currentZoom()));
		}
	
	}

	QRect Layer::offscreenViewport() const
	{
		return myoffscreenViewport;
	}

	void Layer::moveWidgets(const QPoint mapmiddle_px) const
	{
		for (int i=0; i<geometries.count(); i++)
		{
			const Geometry* geom = geometries.at(i);
			if (geom->GeometryType == "Point")
			{
				if (((Point*)geom)->widget()!=0)
				{
					QPoint topleft_relative = QPoint(mapmiddle_px-screenmiddle);
					((Point*)geom)->drawWidget(mapAdapter, topleft_relative);
				}
			}
		}
	}
	Layer::LayerType Layer::layertype() const
	{
		return mylayertype;
	}

	void Layer::setMapAdapter(MapAdapter* mapadapter)
	{
	
		mapAdapter = mapadapter;
	}
}
